<?php

declare(strict_types=1);

namespace app\service\region;

use app\model\Region;
use app\model\RegionUser;
use app\model\User;
use InvalidArgumentException;
use think\facade\Db;
use think\Paginator;

class RegionUserService
{
    public function paginate(array $filters, int $page, int $pageSize): Paginator
    {
        $query = RegionUser::with(['region', 'user.profile'])
            ->order('id', 'desc');

        if (!empty($filters['region_id'])) {
            $query->where('region_id', (int) $filters['region_id']);
        }

        if (!empty($filters['user_id'])) {
            $query->where('user_id', (int) $filters['user_id']);
        }

        if (!empty($filters['role_type'])) {
            $query->where('role_type', trim((string) $filters['role_type']));
        }

        if (isset($filters['status']) && $filters['status'] !== '') {
            $query->where('status', (int) $filters['status']);
        }

        return $query->paginate([
            'list_rows' => $pageSize,
            'page' => $page,
        ]);
    }

    public function findById(int $id): ?RegionUser
    {
        return RegionUser::with(['region', 'user.profile'])->find($id);
    }

    public function create(array $payload): RegionUser
    {
        $data = $this->extractPayload($payload, true);
        $this->ensureRelations($data['region_id'], $data['user_id']);
        $this->assertUnique($data['region_id'], $data['user_id']);

        /** @var RegionUser $relation */
        $relation = Db::transaction(function () use ($data) {
            return RegionUser::create($data);
        });

        return $this->findById((int) $relation->id);
    }

    public function update(RegionUser $relation, array $payload): RegionUser
    {
        $data = $this->extractPayload($payload, false);

        if (array_key_exists('region_id', $data) || array_key_exists('user_id', $data)) {
            $regionId = $data['region_id'] ?? (int) $relation->region_id;
            $userId = $data['user_id'] ?? (int) $relation->user_id;
            $this->ensureRelations($regionId, $userId);

            if ($regionId !== (int) $relation->region_id || $userId !== (int) $relation->user_id) {
                $this->assertUnique($regionId, $userId, (int) $relation->id);
            }
        }

        Db::transaction(function () use ($relation, $data) {
            $relation->save($data);
        });

        return $this->findById((int) $relation->id);
    }

    public function delete(RegionUser $relation): bool
    {
        return (bool) $relation->delete();
    }

    public function format(RegionUser $relation): array
    {
        return [
            'id' => (int) $relation->id,
            'region_id' => (int) $relation->region_id,
            'user_id' => (int) $relation->user_id,
            'role_type' => $relation->role_type,
            'is_primary' => (int) $relation->is_primary,
            'status' => (int) $relation->status,
            'config' => $this->normaliseConfig($relation->config),
            'create_time' => $relation->create_time,
            'update_time' => $relation->update_time,
            'region' => $relation->region ? [
                'id' => (int) $relation->region->id,
                'name' => $relation->region->name,
                'code' => $relation->region->code,
            ] : null,
            'user' => $relation->user ? array_merge(
                $relation->user->toArray(),
                [
                    'profile' => $relation->user->profile ? $relation->user->profile->toArray() : null,
                ]
            ) : null,
        ];
    }

    private function extractPayload(array $payload, bool $isCreate): array
    {
        $data = [];

        if ($isCreate || array_key_exists('region_id', $payload)) {
            $regionId = (int) ($payload['region_id'] ?? 0);
            if ($regionId <= 0) {
                throw new InvalidArgumentException('区域ID不能为空');
            }
            $data['region_id'] = $regionId;
        }

        if ($isCreate || array_key_exists('user_id', $payload)) {
            $userId = (int) ($payload['user_id'] ?? 0);
            if ($userId <= 0) {
                throw new InvalidArgumentException('用户ID不能为空');
            }
            $data['user_id'] = $userId;
        }

        if ($isCreate || array_key_exists('role_type', $payload)) {
            $role = trim((string) ($payload['role_type'] ?? ''));
            if (!in_array($role, [RegionUser::ROLE_MANAGER, RegionUser::ROLE_OPERATOR], true)) {
                throw new InvalidArgumentException('区域成员角色不正确');
            }
            $data['role_type'] = $role;
        }

        if ($isCreate || array_key_exists('is_primary', $payload)) {
            $data['is_primary'] = (int) (!empty($payload['is_primary']));
        }

        if ($isCreate || array_key_exists('status', $payload)) {
            $status = (int) ($payload['status'] ?? 1);
            if (!in_array($status, [0, 1], true)) {
                throw new InvalidArgumentException('区域成员状态不正确');
            }
            $data['status'] = $status;
        }

        if ($isCreate || array_key_exists('config', $payload)) {
            $config = $payload['config'] ?? [];
            if ($config !== null && !is_array($config)) {
                throw new InvalidArgumentException('扩展配置格式不正确');
            }
            $data['config'] = $config ?? [];
        }

        return $data;
    }

    private function ensureRelations(int $regionId, int $userId): void
    {
        if (Region::where('id', $regionId)->count() === 0) {
            throw new InvalidArgumentException('区域不存在');
        }

        if (User::where('id', $userId)->count() === 0) {
            throw new InvalidArgumentException('用户不存在');
        }
    }

    private function assertUnique(int $regionId, int $userId, ?int $excludeId = null): void
    {
        $query = RegionUser::where('region_id', $regionId)
            ->where('user_id', $userId);

        if ($excludeId !== null) {
            $query->where('id', '<>', $excludeId);
        }

        if ($query->count() > 0) {
            throw new InvalidArgumentException('该用户已绑定此区域');
        }
    }

    private function normaliseConfig($config): array
    {
        if (is_array($config)) {
            return $config;
        }

        if ($config === null || $config === '') {
            return [];
        }

        $decoded = json_decode((string) $config, true);
        return is_array($decoded) ? $decoded : [];
    }
}
